package org.sqltomongo.orm;

import cn.org.wangchangjiu.sqltomongo.core.analyzer.*;
import cn.org.wangchangjiu.sqltomongo.core.common.ParserPartTypeEnum;
import cn.org.wangchangjiu.sqltomongo.core.parser.PartSQLParser;
import cn.org.wangchangjiu.sqltomongo.core.parser.PartSQLParserBuilder;
import cn.org.wangchangjiu.sqltomongo.core.parser.SelectSQLTypeParser;
import lombok.Data;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.sqltomongo.binding.MapperAnnotationBuilder;
import org.sqltomongo.binding.MapperProxyFactory;
import org.sqltomongo.cache.Cache;
import org.sqltomongo.cache.data.CacheManager;
import org.sqltomongo.exception.BindingException;
import org.sqltomongo.executor.CachingExecutor;
import org.sqltomongo.executor.DefaultExecutor;
import org.sqltomongo.executor.Executor;
import org.sqltomongo.plugin.Interceptor;
import org.sqltomongo.plugin.InterceptorChain;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Classname Configuration
 * @Description 解析 配置
 * @Date 2022/8/9 17:30
 * @Created by wangchangjiu
 */
public class Configuration {


    private DataCache dataCache;

    private ParserCache parserCache;

    /**
     * Mapper 代理
     */
    private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

    /**
     * 接口SQL映射
     */
    private Map<String, XNode> mapperElement;

    /**
     * 拦截器链条
     */
    private static final InterceptorChain interceptorChain = new InterceptorChain();


    /**
     * 创建执行器 缓存执行器 和 默认执行器
     * 并对执行器增加拦截器处理
     *
     * @param mongoTemplate
     * @param selectSQLTypeParser
     * @return
     */
    public Executor newExecutor(MongoTemplate mongoTemplate, SelectSQLTypeParser selectSQLTypeParser) {
        Executor executor = new DefaultExecutor(mongoTemplate, selectSQLTypeParser, this);
        DataCache dataCache = this.getDataCache();
        if (dataCache !=null && dataCache.cacheEnabled) {
            executor = new CachingExecutor(executor, this);
        }
        executor = (Executor) interceptorChain.pluginAll(executor);
        return executor;
    }


    /**
     * 添加拦截器
     *
     * @param interceptor
     */
    public void addInterceptor(Interceptor interceptor) {
        interceptorChain.addInterceptor(interceptor);
    }

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperProxyFactory.");
        }
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }

    /**
     *  判断是否有 Mapper
     * @param type
     * @param <T>
     * @return
     */
    public <T> boolean hasMapper(Class<T> type) {
        return knownMappers.containsKey(type);
    }

    /**
     *  添加 Mapper
     * @param type
     * @param <T>
     */
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {
            if (hasMapper(type)) {
                throw new BindingException("Type " + type + " is already known to the MapperProxyFactory.");
            }
            boolean loadCompleted = false;
            try {
                knownMappers.put(type, new MapperProxyFactory<>(type));
                // 解析 Mapper 注解
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    knownMappers.remove(type);
                }
            }
        }
    }


    public Map<String, XNode> getMapperElement() {
        return mapperElement;
    }

    public void setMapperElement(Map<String, XNode> mapperElement) {
        this.mapperElement = mapperElement;
    }



    public void addMapperElement(String key, XNode xNode) {
        this.mapperElement.put(key, xNode);
    }

    public DataCache getDataCache() {
        return dataCache;
    }

    public void setDataCache(DataCache dataCache) {
        this.dataCache = dataCache;
    }

    public ParserCache getParserCache() {
        return parserCache;
    }

    public void setParserCache(ParserCache parserCache) {
        this.parserCache = parserCache;
    }


    public static final class InterceptorPartSQLParserBuilder implements PartSQLParserBuilder {

        /**
         *  SQL 解析器缓存
         */
        private static Map<ParserPartTypeEnum, PartSQLParser> partSQLParserCache = new ConcurrentHashMap<>();

        /**
         *  优先缓存获取实例
         * @param typeEnum
         * @return
         */
        @Override
        public PartSQLParser getPartSQLParserInstance(ParserPartTypeEnum typeEnum){
            if(partSQLParserCache.containsKey(typeEnum)){
                return partSQLParserCache.get(typeEnum);
            }
            PartSQLParser partSQLParser = ParserPartTypeEnum.buildNoPlugin(typeEnum);
            partSQLParser = (PartSQLParser) interceptorChain.pluginAll(partSQLParser);
            partSQLParserCache.put(typeEnum, partSQLParser);
            return partSQLParser;
        }
    }


    public static final class InterceptorAnalyzerBuilder implements AnalyzerBuilder {

        /**
         *  创建 SQL 分析器责任链 （被拦截器包裹的）
         * @return
         */
        @Override
        public  Analyzer newAnalyzerInstance() {
            return new AbstractAnalyzer.Builder()
                    .addAnalyzer((Analyzer) interceptorChain.pluginAll(new JoinAnalyzer()))
                    .addAnalyzer((Analyzer) interceptorChain.pluginAll(new MatchAnalyzer()))
                    .addAnalyzer((Analyzer) interceptorChain.pluginAll(new GroupAnalyzer()))
                    .addAnalyzer((Analyzer) interceptorChain.pluginAll(new HavingAnalyzer()))
                    .addAnalyzer((Analyzer) interceptorChain.pluginAll(new SortAnalyzer()))
                    .addAnalyzer((Analyzer) interceptorChain.pluginAll(new LimitAnalyzer()))
                    .addAnalyzer((Analyzer) interceptorChain.pluginAll(new ProjectAnalyzer()))
                    .build();
        }

    }

    /**
     *  数据缓存
     */
    @Data
    public static class DataCache {

        /**
         * 缓存标志
         */
        private boolean cacheEnabled = false;

        /**
         *  缓存管理器
         */
        private CacheManager cacheManager;


        /**
         *  缓存
         */
        private Cache cache;

    }

    /**
     *  SQL 解析缓存
     */
    @Data
    public static class ParserCache {

        private Cache cache;

    }


}
